+2004-07-16 Matthias Clasen <mclasen@redhat.com>
+
+ Add a combo box cell renderer (#139347, Lorenzo Gil Sanchez)
+
+ * gtk/gtkcellrenderercombo.[hc]: New Files.
+
+ * gtk/gtk.h:
+ * gtk/Makefile.am (gtk_public_h_sources, gtk_c_sources):
+ Add the new files.
+
+ * gtk/gtkcombobox.[hc]: Implement GtkCellEditable, add a
+ :has-frame property for suppressing the frame around
+ the child and redo the size allocation logic to take
+ focus width into account.
+
+ * gtk/gtkcomboboxentry.c (gtk_combo_box_entry_init):
+ Acknowledge the GtkComboBox:has-frame property and make
+ the entry fill its allocation vertically.
+
+ * gtk/gtkmarshalers.list: Add BOOLEAN:STRING.
+
+ * gtk/gtktreeview.c (gtk_tree_view_remove_widget): Queue
+ a draw here to keep non-widget windows from leaving
+ shadows behind.
+
2004-07-16 Matthias Clasen <mclasen@redhat.com>
* gtk/gtkcombobox.c (gtk_combo_box_forall): Don't iterate
+2004-07-16 Matthias Clasen <mclasen@redhat.com>
+
+ Add a combo box cell renderer (#139347, Lorenzo Gil Sanchez)
+
+ * gtk/gtkcellrenderercombo.[hc]: New Files.
+
+ * gtk/gtk.h:
+ * gtk/Makefile.am (gtk_public_h_sources, gtk_c_sources):
+ Add the new files.
+
+ * gtk/gtkcombobox.[hc]: Implement GtkCellEditable, add a
+ :has-frame property for suppressing the frame around
+ the child and redo the size allocation logic to take
+ focus width into account.
+
+ * gtk/gtkcomboboxentry.c (gtk_combo_box_entry_init):
+ Acknowledge the GtkComboBox:has-frame property and make
+ the entry fill its allocation vertically.
+
+ * gtk/gtkmarshalers.list: Add BOOLEAN:STRING.
+
+ * gtk/gtktreeview.c (gtk_tree_view_remove_widget): Queue
+ a draw here to keep non-widget windows from leaving
+ shadows behind.
+
2004-07-16 Matthias Clasen <mclasen@redhat.com>
* gtk/gtkcombobox.c (gtk_combo_box_forall): Don't iterate
+2004-07-16 Matthias Clasen <mclasen@redhat.com>
+
+ Add a combo box cell renderer (#139347, Lorenzo Gil Sanchez)
+
+ * gtk/gtkcellrenderercombo.[hc]: New Files.
+
+ * gtk/gtk.h:
+ * gtk/Makefile.am (gtk_public_h_sources, gtk_c_sources):
+ Add the new files.
+
+ * gtk/gtkcombobox.[hc]: Implement GtkCellEditable, add a
+ :has-frame property for suppressing the frame around
+ the child and redo the size allocation logic to take
+ focus width into account.
+
+ * gtk/gtkcomboboxentry.c (gtk_combo_box_entry_init):
+ Acknowledge the GtkComboBox:has-frame property and make
+ the entry fill its allocation vertically.
+
+ * gtk/gtkmarshalers.list: Add BOOLEAN:STRING.
+
+ * gtk/gtktreeview.c (gtk_tree_view_remove_widget): Queue
+ a draw here to keep non-widget windows from leaving
+ shadows behind.
+
2004-07-16 Matthias Clasen <mclasen@redhat.com>
* gtk/gtkcombobox.c (gtk_combo_box_forall): Don't iterate
+2004-07-16 Matthias Clasen <mclasen@redhat.com>
+
+ Add a combo box cell renderer (#139347, Lorenzo Gil Sanchez)
+
+ * gtk/gtkcellrenderercombo.[hc]: New Files.
+
+ * gtk/gtk.h:
+ * gtk/Makefile.am (gtk_public_h_sources, gtk_c_sources):
+ Add the new files.
+
+ * gtk/gtkcombobox.[hc]: Implement GtkCellEditable, add a
+ :has-frame property for suppressing the frame around
+ the child and redo the size allocation logic to take
+ focus width into account.
+
+ * gtk/gtkcomboboxentry.c (gtk_combo_box_entry_init):
+ Acknowledge the GtkComboBox:has-frame property and make
+ the entry fill its allocation vertically.
+
+ * gtk/gtkmarshalers.list: Add BOOLEAN:STRING.
+
+ * gtk/gtktreeview.c (gtk_tree_view_remove_widget): Queue
+ a draw here to keep non-widget windows from leaving
+ shadows behind.
+
2004-07-16 Matthias Clasen <mclasen@redhat.com>
* gtk/gtkcombobox.c (gtk_combo_box_forall): Don't iterate
gtkcelleditable.h \
gtkcelllayout.h \
gtkcellrenderer.h \
+ gtkcellrenderercombo.h \
gtkcellrendererpixbuf.h \
gtkcellrendererprogress.h \
gtkcellrenderertext.h \
gtkcelleditable.c \
gtkcelllayout.c \
gtkcellrenderer.c \
+ gtkcellrenderercombo.c \
gtkcellrendererpixbuf.c \
gtkcellrendererprogress.c \
gtkcellrendererseptext.c\
#include <gtk/gtkcalendar.h>
#include <gtk/gtkcelllayout.h>
#include <gtk/gtkcellrenderer.h>
+#include <gtk/gtkcellrenderercombo.h>
#include <gtk/gtkcellrendererpixbuf.h>
#include <gtk/gtkcellrendererprogress.h>
#include <gtk/gtkcellrenderertext.h>
--- /dev/null
+/* GtkCellRendererCombo
+ * Copyright (C) 2004 Lorenzo Gil Sanchez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "gtkintl.h"
+#include "gtkbin.h"
+#include "gtkentry.h"
+#include "gtkcelllayout.h"
+#include "gtkcellrenderercombo.h"
+#include "gtkcellrenderertext.h"
+#include "gtkcombobox.h"
+#include "gtkcomboboxentry.h"
+
+static void gtk_cell_renderer_combo_class_init (GtkCellRendererComboClass *klass);
+static void gtk_cell_renderer_combo_init (GtkCellRendererCombo *self);
+static void gtk_cell_renderer_combo_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gtk_cell_renderer_combo_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static GtkCellEditable *gtk_cell_renderer_combo_start_editing (GtkCellRenderer *cell,
+ GdkEvent *event,
+ GtkWidget *widget,
+ const gchar *path,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GtkCellRendererState flags);
+
+enum {
+ PROP_0,
+ PROP_MODEL,
+ PROP_TEXT_COLUMN,
+ PROP_HAS_ENTRY
+};
+
+static GObjectClass *parent_class = NULL;
+
+#define GTK_CELL_RENDERER_COMBO_PATH "gtk-cell-renderer-combo-path"
+
+GType
+gtk_cell_renderer_combo_get_type (void)
+{
+ static GType gtk_cell_renderer_combo_type = 0;
+
+ if (!gtk_cell_renderer_combo_type)
+ {
+ static const GTypeInfo gtk_cell_renderer_combo_info =
+ {
+ sizeof (GtkCellRendererComboClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gtk_cell_renderer_combo_class_init,
+ NULL,
+ NULL,
+ sizeof (GtkCellRendererCombo),
+ 0,
+ (GInstanceInitFunc) gtk_cell_renderer_combo_init
+ };
+ gtk_cell_renderer_combo_type =
+ g_type_register_static (GTK_TYPE_CELL_RENDERER_TEXT,
+ "GtkCellRendererCombo",
+ >k_cell_renderer_combo_info,
+ 0);
+ }
+ return gtk_cell_renderer_combo_type;
+}
+
+static void
+gtk_cell_renderer_combo_class_init (GtkCellRendererComboClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->get_property = gtk_cell_renderer_combo_get_property;
+ object_class->set_property = gtk_cell_renderer_combo_set_property;
+
+ cell_class->start_editing = gtk_cell_renderer_combo_start_editing;
+
+ /**
+ * GtkCellRendererCombo:model:
+ *
+ * The :model property holds a tree model containing the possible
+ * values for the combo box. Use the :text_column property to specify
+ * the column holding the values.
+ *
+ * Since: 2.6
+ */
+ g_object_class_install_property (object_class,
+ PROP_MODEL,
+ g_param_spec_object ("model",
+ P_("Model"),
+ P_("The model containing the possible values for the combo box"),
+ GTK_TYPE_TREE_MODEL,
+ G_PARAM_READWRITE));
+
+ /**
+ * GtkCellRendererCombo:text_column:
+ *
+ * The :text_column property specifies the model column which
+ * holds the possible values for the combo box. Note that this
+ * refers to the model specified in the :model property,
+ * <emphasis>not</emphasis> the model backing the tree view to
+ * which this cell renderer is attached.
+ *
+ * Since: 2.6
+ */
+ g_object_class_install_property (object_class,
+ PROP_TEXT_COLUMN,
+ g_param_spec_int ("text_column",
+ P_("Text Column"),
+ P_("A column in the data source model to get the strings from"),
+ -1,
+ G_MAXINT,
+ -1,
+ G_PARAM_READWRITE));
+
+ /**
+ * GtkCellRendererCombo:has_entry:
+ *
+ * If the :has_entry property is %TRUe, the cell renderer will
+ * include an entry and allow to enter values other than the ones
+ * in the popup list.
+ *
+ * Since: 2.6
+ */
+ g_object_class_install_property (object_class,
+ PROP_HAS_ENTRY,
+ g_param_spec_boolean ("has_entry",
+ P_("Has Entry"),
+ P_("If %FALSE, don't allow to enter strings other than the chosen ones"),
+ TRUE,
+ G_PARAM_READWRITE));
+
+}
+
+static void
+gtk_cell_renderer_combo_init (GtkCellRendererCombo *self)
+{
+ self->model = NULL;
+ self->text_column = -1;
+ self->focus_out_id = 0;
+}
+
+/**
+ * gtk_cell_renderer_combo_new:
+ *
+ * Creates a new #GtkCellRendererCombo
+ * Adjust how text is drawn using object properties.
+ * Object properties can be set globally (with g_object_set()).
+ * Also, with #GtkTreeViewColumn, you can bind a property to a value
+ * in a #GtkTreeModel. For example, you can bind the "text" property
+ * on the cell renderer to a string value in the model, thus rendering
+ * a different string in each row of the #GtkTreeView.
+ *
+ * Returns: the new cell renderer
+ *
+ * Since: 2.6
+ */
+GtkCellRenderer *
+gtk_cell_renderer_combo_new (void)
+{
+ return g_object_new (GTK_TYPE_CELL_RENDERER_COMBO, NULL);
+}
+
+static void
+gtk_cell_renderer_combo_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCellRendererCombo *cell;
+
+ g_return_if_fail (GTK_IS_CELL_RENDERER_COMBO (object));
+
+ cell = GTK_CELL_RENDERER_COMBO (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ g_value_set_object (value, cell->model);
+ break;
+ case PROP_TEXT_COLUMN:
+ g_value_set_int (value, cell->text_column);
+ break;
+ case PROP_HAS_ENTRY:
+ g_value_set_boolean (value, cell->has_entry);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gtk_cell_renderer_combo_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCellRendererCombo *cell;
+
+ g_return_if_fail (GTK_IS_CELL_RENDERER_COMBO (object));
+
+ cell = GTK_CELL_RENDERER_COMBO (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ cell->model = g_value_get_object (value);
+ break;
+ case PROP_TEXT_COLUMN:
+ cell->text_column = g_value_get_int (value);
+ break;
+ case PROP_HAS_ENTRY:
+ cell->has_entry = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gtk_cell_renderer_combo_editing_done (GtkCellEditable *combo,
+ gpointer data)
+{
+ const gchar *path;
+ gchar *new_text = NULL;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkCellRendererCombo *cell;
+ GtkEntry *entry;
+
+ cell = GTK_CELL_RENDERER_COMBO (data);
+
+ if (cell->focus_out_id > 0)
+ {
+ g_signal_handler_disconnect (combo, cell->focus_out_id);
+ cell->focus_out_id = 0;
+ }
+
+ if (_gtk_combo_box_editing_canceled (GTK_COMBO_BOX (combo)))
+ {
+ gtk_cell_renderer_editing_canceled (GTK_CELL_RENDERER (data));
+ return;
+ }
+
+ if (GTK_IS_COMBO_BOX_ENTRY (combo))
+ {
+ entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo)));
+ new_text = g_strdup (gtk_entry_get_text (entry));
+ }
+ else
+ {
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+ if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
+ gtk_tree_model_get (model, &iter, cell->text_column, &new_text, -1);
+ }
+
+ path = g_object_get_data (G_OBJECT (combo), GTK_CELL_RENDERER_COMBO_PATH);
+ g_signal_emit_by_name (cell, "edited", path, new_text);
+
+ g_free (new_text);
+}
+
+static gboolean
+gtk_cell_renderer_combo_focus_out_event (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer data)
+{
+
+ gtk_cell_renderer_combo_editing_done (GTK_CELL_EDITABLE (widget), data);
+
+ return FALSE;
+}
+
+typedef struct
+{
+ GtkCellRendererCombo *cell;
+ gboolean found;
+ GtkTreeIter iter;
+} SearchData;
+
+static gboolean
+find_text (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ SearchData *search_data = (SearchData *)data;
+ gchar *text;
+
+ gtk_tree_model_get (model, iter, search_data->cell->text_column, &text, -1);
+ if (text && GTK_CELL_RENDERER_TEXT (search_data->cell)->text &&
+ strcmp (text, GTK_CELL_RENDERER_TEXT (search_data->cell)->text) == 0)
+ {
+ search_data->iter = *iter;
+ search_data->found = TRUE;
+ }
+
+ return search_data->found;
+}
+
+static GtkCellEditable *
+gtk_cell_renderer_combo_start_editing (GtkCellRenderer *cell,
+ GdkEvent *event,
+ GtkWidget *widget,
+ const gchar *path,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GtkCellRendererState flags)
+{
+ GtkCellRendererCombo *cell_combo;
+ GtkCellRendererText *cell_text;
+ GtkWidget *combo;
+ SearchData search_data;
+
+ cell_text = GTK_CELL_RENDERER_TEXT (cell);
+ if (cell_text->editable == FALSE)
+ return NULL;
+
+ cell_combo = GTK_CELL_RENDERER_COMBO (cell);
+ if (cell_combo->model == NULL || cell_combo->text_column < 0)
+ return NULL;
+
+ if (cell_combo->has_entry)
+ {
+ combo = gtk_combo_box_entry_new_with_model (cell_combo->model, cell_combo->text_column);
+
+ if (cell_text->text)
+ gtk_entry_set_text (GTK_ENTRY (GTK_BIN (combo)->child),
+ cell_text->text);
+ }
+ else
+ {
+ cell = gtk_cell_renderer_text_new ();
+ combo = gtk_combo_box_new_with_model (cell_combo->model);
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo),
+ cell, "text", cell_combo->text_column,
+ NULL);
+
+ /* determine the current value */
+ search_data.cell = cell_combo;
+ search_data.found = FALSE;
+ gtk_tree_model_foreach (cell_combo->model, find_text, &search_data);
+ if (search_data.found)
+ gtk_combo_box_set_active_iter (combo, &(search_data.iter));
+ }
+
+ g_object_set (combo, "has_frame", FALSE, NULL);
+ g_object_set_data_full (G_OBJECT (combo),
+ GTK_CELL_RENDERER_COMBO_PATH,
+ g_strdup (path), g_free);
+
+ gtk_widget_show (combo);
+
+ g_signal_connect (GTK_CELL_EDITABLE (combo), "editing_done",
+ G_CALLBACK (gtk_cell_renderer_combo_editing_done),
+ cell_combo);
+ cell_combo->focus_out_id =
+ g_signal_connect (combo, "focus_out_event",
+ G_CALLBACK (gtk_cell_renderer_combo_focus_out_event),
+ cell_combo);
+
+ return GTK_CELL_EDITABLE (combo);
+}
--- /dev/null
+/* GtkCellRendererCombo
+ * Copyright (C) 2004 Lorenzo Gil Sanchez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef GTK_CELL_RENDERER_COMBO_H
+#define GTK_CELL_RENDERER_COMBO_H
+
+#include <gtk/gtktreemodel.h>
+#include <gtk/gtkcellrenderertext.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CELL_RENDERER_COMBO (gtk_cell_renderer_combo_get_type ())
+#define GTK_CELL_RENDERER_COMBO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_COMBO, GtkCellRendererCombo))
+#define GTK_CELL_RENDERER_COMBO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_COMBO, GtkCellRendererComboClass))
+#define GTK_IS_CELL_RENDERER_COMBO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_COMBO))
+#define GTK_IS_CELL_RENDERER_COMBO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_RENDERER_COMBO))
+#define GTK_CELL_RENDERER_COMBO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_RENDERER_COMBO, GtkCellRendererTextClass))
+
+typedef struct _GtkCellRendererCombo GtkCellRendererCombo;
+typedef struct _GtkCellRendererComboClass GtkCellRendererComboClass;
+
+struct _GtkCellRendererCombo
+{
+ GtkCellRendererText parent;
+
+ GtkTreeModel *model;
+ gint text_column;
+ gboolean has_entry;
+
+ /*< private >*/
+ guint focus_out_id;
+};
+
+struct _GtkCellRendererComboClass
+{
+ GtkCellRendererTextClass parent;
+};
+
+GType gtk_cell_renderer_combo_get_type (void);
+GtkCellRenderer *gtk_cell_renderer_combo_new (void);
+
+G_END_DECLS
+
+#endif
guint deleted_id;
guint reordered_id;
guint changed_id;
+ guint popup_idle_id;
gint width;
GSList *cells;
guint popup_in_progress : 1;
guint destroying : 1;
guint add_tearoffs : 1;
+ guint has_frame : 1;
+ guint is_cell_renderer : 1;
+ guint editing_canceled : 1;
};
/* While debugging this evil code, I have learned that
PROP_COLUMN_SPAN_COLUMN,
PROP_ROW_SEPARATOR_COLUMN,
PROP_ACTIVE,
- PROP_ADD_TEAROFFS
+ PROP_ADD_TEAROFFS,
+ PROP_HAS_FRAME
};
static GtkBinClass *parent_class = NULL;
/* common */
static void gtk_combo_box_class_init (GtkComboBoxClass *klass);
static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
+static void gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface);
static void gtk_combo_box_init (GtkComboBox *combo_box);
static void gtk_combo_box_finalize (GObject *object);
static void gtk_combo_box_destroy (GtkObject *object);
GdkEventKey *event,
gpointer data);
+static void gtk_combo_box_check_appearance (GtkComboBox *combo_box);
+
/* listening to the model */
static void gtk_combo_box_model_row_inserted (GtkTreeModel *model,
GtkTreePath *path,
static void cell_view_sync_cells (GtkComboBox *combo_box,
GtkCellView *cell_view);
+/* GtkCellEditable method implementations */
+static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
+ GdkEvent *event);
+
+
GType
gtk_combo_box_get_type (void)
{
NULL
};
+ static const GInterfaceInfo cell_editable_info =
+ {
+ (GInterfaceInitFunc) gtk_combo_box_cell_editable_init,
+ NULL,
+ NULL
+ };
+
combo_box_type = g_type_register_static (GTK_TYPE_BIN,
"GtkComboBox",
&combo_box_info,
g_type_add_interface_static (combo_box_type,
GTK_TYPE_CELL_LAYOUT,
&cell_layout_info);
+
+
+ g_type_add_interface_static (combo_box_type,
+ GTK_TYPE_CELL_EDITABLE,
+ &cell_editable_info);
+
+
}
return combo_box_type;
PROP_ADD_TEAROFFS,
g_param_spec_boolean ("add-tearoffs",
P_("Add tearoffs to menus"),
- P_("Whether combobox dropdowns should have a tearoff menu item"),
+ P_("Whether dropdowns should have a tearoff menu item"),
FALSE,
G_PARAM_READWRITE));
+ /**
+ * GtkComboBox:has-frame:
+ *
+ * The :has-frame property controls whether a frame
+ * is drawn around the entry.
+ *
+ * Since: 2.6
+ */
+ g_object_class_install_property (object_class,
+ PROP_HAS_FRAME,
+ g_param_spec_boolean ("has-frame",
+ P_("Has Frame"),
+ P_("Whether the combo box draws a frame around the child"),
+ TRUE,
+ G_PARAM_READWRITE));
+
gtk_widget_class_install_style_property (widget_class,
g_param_spec_boolean ("appears-as-list",
P_("Appears as list"),
- P_("Whether combobox dropdowns should look like lists rather than menus"),
+ P_("Whether dropdowns should look like lists rather than menus"),
FALSE,
G_PARAM_READABLE));
iface->reorder = gtk_combo_box_cell_layout_reorder;
}
+static void
+gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface)
+{
+ iface->start_editing = gtk_combo_box_start_editing;
+}
+
static void
gtk_combo_box_init (GtkComboBox *combo_box)
{
combo_box->priv->col_column = -1;
combo_box->priv->row_column = -1;
combo_box->priv->separator_column = -1;
+
+ combo_box->priv->add_tearoffs = FALSE;
+ combo_box->priv->has_frame = TRUE;
+ combo_box->priv->is_cell_renderer = FALSE;
+ combo_box->priv->editing_canceled = FALSE;
}
static void
gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value));
break;
+ case PROP_HAS_FRAME:
+ combo_box->priv->has_frame = g_value_get_boolean (value);
+ break;
+
default:
break;
}
g_value_set_boolean (value, gtk_combo_box_get_add_tearoffs (combo_box));
break;
+ case PROP_HAS_FRAME:
+ g_value_set_boolean (value, combo_box->priv->has_frame);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
{
gtk_widget_unparent (combo_box->priv->cell_view_frame);
combo_box->priv->cell_view_frame = NULL;
+ combo_box->priv->box = NULL;
}
}
}
gdk_window_get_origin (sample->window, x, y);
- if (combo_box->priv->cell_view_frame)
+ if (combo_box->priv->cell_view_frame && combo_box->priv->has_frame)
{
*x -= GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
GtkRequisition *requisition)
{
gint width, height;
+ gint focus_width, focus_pad;
GtkRequisition bin_req;
GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
gtk_combo_box_remeasure (combo_box);
bin_req.width = MAX (bin_req.width, combo_box->priv->width);
+ gtk_widget_style_get (GTK_WIDGET (widget),
+ "focus-line-width", &focus_width,
+ "focus-padding", &focus_pad,
+ NULL);
+
if (!combo_box->priv->tree_view)
{
/* menu mode */
gint border_width, xthickness, ythickness;
gtk_widget_size_request (combo_box->priv->button, &button_req);
- border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
+ border_width = GTK_CONTAINER (combo_box)->border_width;
xthickness = combo_box->priv->button->style->xthickness;
ythickness = combo_box->priv->button->style->ythickness;
width = bin_req.width + sep_req.width + arrow_req.width;
- height += border_width + 1 + ythickness * 2 + 4;
- width += border_width + 1 + xthickness * 2 + 4;
+ height += 2*(border_width + ythickness + focus_width + focus_pad);
+ width += 2*(border_width + xthickness + focus_width + focus_pad);
requisition->width = width;
requisition->height = height;
if (combo_box->priv->cell_view_frame)
{
gtk_widget_size_request (combo_box->priv->cell_view_frame, &frame_req);
- requisition->width += 2 *
- (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
- GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
- requisition->height += 2 *
- (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
- GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
+ if (combo_box->priv->has_frame)
+ {
+ requisition->width += 2 *
+ (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
+ GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
+ requisition->height += 2 *
+ (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
+ GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
+ }
}
/* the button */
GtkAllocation *allocation)
{
GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
+ gint focus_width, focus_pad;
GtkAllocation child;
GtkRequisition req;
gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
widget->allocation = *allocation;
+ gtk_widget_style_get (GTK_WIDGET (widget),
+ "focus-line-width", &focus_width,
+ "focus-padding", &focus_pad,
+ NULL);
+
if (!combo_box->priv->tree_view)
{
if (combo_box->priv->cell_view)
xthickness = combo_box->priv->button->style->xthickness;
ythickness = combo_box->priv->button->style->ythickness;
- child.x = allocation->x + border_width + 1 + xthickness + 2;
- child.y = allocation->y + border_width + 1 + ythickness + 2;
+ child.x = allocation->x;
+ child.y = allocation->y;
+ width = allocation->width;
+ child.height = allocation->height;
+
+ if (!combo_box->priv->is_cell_renderer)
+ {
+ child.x += border_width + xthickness + focus_width + focus_pad;
+ child.y += border_width + ythickness + focus_width + focus_pad;
+ width -= 2 * (child.x - allocation->x);
+ child.height -= 2 * (child.y - allocation->y);
+ }
- width = allocation->width - (border_width + 1 + xthickness * 2 + 4);
/* handle the children */
gtk_widget_size_request (combo_box->priv->arrow, &req);
child.width = req.width;
- child.height = allocation->height - 2 * (child.y - allocation->y);
if (!is_rtl)
child.x += width - req.width;
gtk_widget_size_allocate (combo_box->priv->arrow, &child);
{
child.x += req.width;
child.width = allocation->x + allocation->width
- - (border_width + 1 + xthickness + 2) - child.x;
+ - (border_width + xthickness + focus_width + focus_pad)
+ - child.x;
}
else
{
child.width = child.x;
- child.x = allocation->x + border_width + 1 + xthickness + 2;
+ child.x = allocation->x
+ + border_width + xthickness + focus_width + focus_pad;
child.width -= child.x;
}
gtk_widget_size_allocate (combo_box->priv->cell_view_frame, &child);
/* the sample */
- child.x +=
- GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
- GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
- child.y +=
- GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
- GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness;
- child.width -= 2 * (
- GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
- GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
- child.height -= 2 * (
- GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
- GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
+ if (combo_box->priv->has_frame)
+ {
+ child.x +=
+ GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
+ GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
+ child.y +=
+ GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
+ GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness;
+ child.width -= 2 * (
+ GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
+ GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
+ child.height -= 2 * (
+ GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
+ GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
+ }
}
-
+
gtk_widget_size_allocate (GTK_BIN (combo_box)->child, &child);
}
}
if (!combo_box->priv->tree_view)
{
gtk_container_propagate_expose (GTK_CONTAINER (widget),
- combo_box->priv->button, event);
+ combo_box->priv->button, event);
}
else
{
combo_box->priv->button = gtk_toggle_button_new ();
g_signal_connect (combo_box->priv->button, "toggled",
G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
- g_signal_connect_after (combo_box->priv->button, "key_press_event",
+ g_signal_connect_after (combo_box->priv->button,
+ "key_press_event",
G_CALLBACK (gtk_combo_box_key_press), combo_box);
gtk_widget_set_parent (combo_box->priv->button,
GTK_BIN (combo_box)->child->parent);
index = g_list_index (children, item);
gtk_combo_box_set_active (combo_box, index);
+
+ combo_box->priv->editing_canceled = FALSE;
}
static void
if (combo_box->priv->cell_view)
{
- combo_box->priv->cell_view_frame = gtk_frame_new (NULL);
- gtk_widget_set_parent (combo_box->priv->cell_view_frame,
- GTK_BIN (combo_box)->child->parent);
- gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->cell_view_frame),
- GTK_SHADOW_IN);
-
gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view),
>K_WIDGET (combo_box)->style->base[GTK_WIDGET_STATE (combo_box)]);
gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->box),
FALSE);
+ if (combo_box->priv->has_frame)
+ {
+ combo_box->priv->cell_view_frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->cell_view_frame),
+ GTK_SHADOW_IN);
+ }
+ else
+ {
+ combo_box->priv->cell_view_frame = gtk_event_box_new ();
+ gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame),
+ FALSE);
+ }
+
+ gtk_widget_set_parent (combo_box->priv->cell_view_frame,
+ GTK_BIN (combo_box)->child->parent);
gtk_container_add (GTK_CONTAINER (combo_box->priv->cell_view_frame),
combo_box->priv->box);
-
gtk_widget_show_all (combo_box->priv->cell_view_frame);
g_signal_connect (combo_box->priv->box, "button_press_event",
{
GtkComboBox *combo_box = GTK_COMBO_BOX (object);
+ if (combo_box->priv->popup_idle_id > 0)
+ {
+ g_source_remove (combo_box->priv->popup_idle_id);
+ combo_box->priv->popup_idle_id = 0;
+ }
+
gtk_combo_box_popdown (combo_box);
+
combo_box->priv->destroying = 1;
+
GTK_OBJECT_CLASS (parent_class)->destroy (object);
combo_box->priv->cell_view = NULL;
+
combo_box->priv->destroying = 0;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
+static gboolean
+gtk_cell_editable_key_press (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer data)
+{
+ GtkComboBox *combo_box = GTK_COMBO_BOX (data);
+
+ if (event->keyval == GDK_Escape)
+ {
+ combo_box->priv->editing_canceled = TRUE;
+ gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
+ gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
+
+ return TRUE;
+ }
+ else if (event->keyval == GDK_Return)
+ {
+ gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
+ if (GTK_IS_CELL_EDITABLE (combo_box))
+ gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
+
+ return TRUE;
+ }
+ else if (event->keyval == GDK_space)
+ {
+ /* ignore */
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+popdown_idle (gpointer data)
+{
+ GtkComboBox *combo_box = GTK_COMBO_BOX (data);
+
+ gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
+ gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
+
+ g_object_unref (combo_box);
+
+ return FALSE;
+}
+
+static void
+popdown_handler (GtkWidget *widget,
+ gpointer data)
+{
+ g_idle_add (popdown_idle, g_object_ref (data));
+}
+
+static gboolean
+popup_idle (gpointer data)
+{
+ GtkComboBox *combo_box = GTK_COMBO_BOX (data);
+
+ if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
+ combo_box->priv->cell_view)
+ g_signal_connect_object (combo_box->priv->popup_widget,
+ "unmap", G_CALLBACK (popdown_handler),
+ combo_box, 0);
+
+ /* we unset this if a menu item is activated */
+ combo_box->priv->editing_canceled = TRUE;
+ gtk_combo_box_popup (combo_box);
+
+ return FALSE;
+}
+
+static void
+gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
+ GdkEvent *event)
+{
+ GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
+
+ combo_box->priv->is_cell_renderer = TRUE;
+
+ if (combo_box->priv->cell_view)
+ {
+ g_signal_connect (combo_box->priv->button, "key_press_event",
+ G_CALLBACK (gtk_cell_editable_key_press),
+ cell_editable);
+
+ gtk_widget_grab_focus (combo_box->priv->button);
+ }
+ else
+ {
+ g_signal_connect (GTK_BIN (combo_box)->child, "key_press_event",
+ G_CALLBACK (gtk_cell_editable_key_press),
+ cell_editable);
+
+ gtk_widget_grab_focus (GTK_WIDGET (GTK_BIN (combo_box)->child));
+ GTK_WIDGET_UNSET_FLAGS (combo_box->priv->button, GTK_CAN_FOCUS);
+ }
+
+ /* we do the immediate popup only for the optionmenu-like
+ * appearance
+ */
+ if (combo_box->priv->is_cell_renderer &&
+ combo_box->priv->cell_view && !combo_box->priv->tree_view)
+ combo_box->priv->popup_idle_id = g_idle_add (popup_idle, combo_box);
+}
+
+
+/**
+ * gtk_combo_box_get_add_tearoffs:
+ * @combo_box: a #GtkComboBox
+ *
+ * Gets the current value of the :add-tearoffs property.
+ *
+ * Return value: the current value of the :add-tearoffs property.
+ **/
gboolean
gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
{
return combo_box->priv->add_tearoffs;
}
+/**
+ * gtk_combo_box_set_add_tearoffs:
+ * @combo_box: a #GtkComboBox
+ * @add_tearoffs: %TRUE to add tearoff menu items
+ *
+ * Sets whether the popup menu should have a tearoff
+ * menu item.
+ *
+ * Since: 2.6
+ **/
void
gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
gboolean add_tearoffs)
{
combo_box->priv->separator_column = column;
- gtk_widget_queue_draw (combo_box);
+ gtk_widget_queue_draw (GTK_WIDGET (combo_box));
g_object_notify (G_OBJECT (combo_box), "row_separator_column");
}
return combo_box->priv->separator_column;
}
+
+gboolean
+_gtk_combo_box_editing_canceled (GtkComboBox *combo_box)
+{
+ g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), TRUE);
+
+ return combo_box->priv->editing_canceled;
+}
void gtk_combo_box_popup (GtkComboBox *combo_box);
void gtk_combo_box_popdown (GtkComboBox *combo_box);
+/* private */
+gboolean _gtk_combo_box_editing_canceled (GtkComboBox *combo_box);
+
G_END_DECLS
#endif /* __GTK_COMBO_BOX_H__ */
gpointer user_data);
static gboolean gtk_combo_box_entry_mnemonic_activate (GtkWidget *entry,
gboolean group_cycling);
+static void has_frame_changed (GtkComboBoxEntry *entry_box,
+ GParamSpec *pspec,
+ gpointer data);
enum
{
entry_box->priv->text_column = -1;
entry_box->priv->entry = gtk_entry_new ();
+ /* this flag is a hack to tell the entry to fill its allocation.
+ */
+ GTK_ENTRY (entry_box->priv->entry)->is_cell_renderer = TRUE;
gtk_container_add (GTK_CONTAINER (entry_box), entry_box->priv->entry);
gtk_widget_show (entry_box->priv->entry);
entry_box);
g_signal_connect (entry_box, "changed",
G_CALLBACK (gtk_combo_box_entry_active_changed), NULL);
+ has_frame_changed (entry_box, NULL, NULL);
+ g_signal_connect (entry_box, "notify::has-frame", G_CALLBACK (has_frame_changed), NULL);
}
static void
combo_box);
}
+static void
+has_frame_changed (GtkComboBoxEntry *entry_box,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ gboolean has_frame;
+
+ g_object_get (entry_box, "has_frame", &has_frame, NULL);
+
+ gtk_entry_set_has_frame (GTK_ENTRY (entry_box->priv->entry), has_frame);
+}
+
static void
gtk_combo_box_entry_contents_changed (GtkEntry *entry,
gpointer user_data)
BOOLEAN:BOOLEAN
BOOLEAN:NONE
BOOLEAN:BOOLEAN,BOOLEAN,BOOLEAN
+BOOLEAN:STRING
ENUM:ENUM
INT:POINTER
NONE:BOOLEAN
gint height)
{
GtkTreeViewChild *child;
-
+
g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
g_return_if_fail (GTK_IS_WIDGET (child_widget));
if (GTK_WIDGET_REALIZED (tree_view))
gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
-
+
gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
}
gtk_widget_grab_focus (GTK_WIDGET (tree_view));
gtk_container_remove (GTK_CONTAINER (tree_view),
- GTK_WIDGET (cell_editable));
+ GTK_WIDGET (cell_editable));
+ /* FIXME should only redraw a single node */
+ gtk_widget_queue_draw (GTK_WIDGET (tree_view));
}
static gboolean